package com.team.coupon.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.team.coupon.domain.BizCoupon;
import com.team.coupon.exception.CustomException;
import com.team.coupon.mapper.BizCouponMapper;
import com.team.coupon.service.BizCouponService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.yaml.snakeyaml.events.Event;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.team.coupon.util.RedisConstants.*;

@Slf4j
@Service
public class BizCouponServiceImpl implements BizCouponService {

    @Autowired
    private BizCouponMapper bizCouponMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 查看今天的优惠券秒杀活动，返回优惠券列表
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<BizCoupon> listCoupons() {
        //先从redis中查询
        String today = DateUtil.format(new Date(), DATE_FORMAT);
        String redisKey = BIZ_COUPON_SECKILL_KEY + today;
        //null对象
        String emptyCacheKey = redisKey + CACHE_NULL_VALUE;
        // 锁对像，用于预防缓存穿透
        String lockKey = COUPON_SECKILL_LOCK_KEY + today;
        try{
            // 检查 Redis 中是否存在空值缓存
            if (Boolean.TRUE.equals(redisTemplate.hasKey(emptyCacheKey))) {
                // 如果存在空值缓存，直接返回空列表
                return Collections.emptyList();
            }
            // 检查 Redis 中是否存在当天的秒杀优惠券
            if (Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                // 如果存在，直接从 Redis 中获取数据
                List<Object> couponList = redisTemplate.opsForHash().values(redisKey);
                return couponList.stream()
                        .map(couponJson -> JSONUtil.toBean((String) couponJson, BizCoupon.class))
                        .collect(Collectors.toList());
            }
            // 如果 Redis 中不存在，从数据库中查询并缓存到 Redis
            // 先尝试获取锁
            RLock lock = redissonClient.getLock(lockKey);
            boolean isLock = false;
            try {
                // 尝试获取锁，最多等待 1 秒，锁自动释放时间 5 秒
                isLock = lock.tryLock(1, 5, TimeUnit.SECONDS);
                if (!isLock) {
                    throw new CustomException("系统繁忙，请稍后再试");
                }
                // 再次检查 Redis 中是否存在空值缓存
                if (Boolean.TRUE.equals(redisTemplate.hasKey(emptyCacheKey))) {
                    // 如果存在空值缓存，直接返回空列表
                    return Collections.emptyList();
                }
                // 再次检查 Redis 中是否存在当天的秒杀优惠券
                if (Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    // 如果存在，直接从 Redis 中获取数据
                    List<Object> couponList = redisTemplate.opsForHash().values(redisKey);
                    return couponList.stream()
                            .map(couponJson -> JSONUtil.toBean((String) couponJson, BizCoupon.class))
                            .collect(Collectors.toList());
                }
                List<BizCoupon> coupons = bizCouponMapper.selectCouponByTime(today, 0, Integer.MAX_VALUE);
                if (coupons.isEmpty()) {
                    // 如果数据库中也没有数据，设置空值缓存
                    redisTemplate.opsForValue().set(emptyCacheKey, "true", CACHE_NULL_TTL , TimeUnit.MINUTES); // 设置过期时间为 2 分钟
                    return Collections.emptyList();
                }
                // 将查询到的优惠券列表缓存到 Redis
                coupons.forEach(coupon -> {
                    redisTemplate.opsForHash().put(redisKey, String.valueOf(coupon.getId()), JSONUtil.toJsonStr(coupon));
                });
                // 确保日期已添加到日期集合中
                redisTemplate.opsForSet().add(ALL_COUPON_DATES_KEY, today);
                return coupons;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new CustomException("获取锁失败，请稍后再试");
            } finally {
                if (isLock && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }

        }catch (Exception e){
            log.error("查询当天秒杀优惠券失败", e);
            // 降级方案：返回缓存中的旧数据或空列表
            return fallbackToListCoupons(redisKey, emptyCacheKey);
        }
    }

    /**
     * 添加优惠券功能，判断优惠券是否合法，合法则插入数据库，同时判断是否属于今天的秒杀活动，如果是则更新redis
     * @param bizCoupon
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String addCoupon(BizCoupon bizCoupon) {
        try {
            // 校验优惠券是否合法
            isValidCoupon(bizCoupon);
            // 插入数据库
            bizCouponMapper.insert(bizCoupon);
            if (isTodayFlashSale(bizCoupon)) {
                // 获取优惠券的日期（使用优惠券自身的 instantStart 字段，而非当前时间）
                String dateFormat = DateUtil.format(bizCoupon.getInstantStart(), DATE_FORMAT);
                // 将优惠券加入到 Redis 的 Hash 中
                String redisKey = BIZ_COUPON_SECKILL_KEY + dateFormat;
                //如果属于今天的秒杀活动，更新redis
                redisTemplate.opsForHash().put(
                        redisKey,
                        String.valueOf(bizCoupon.getId()),
                        JSONUtil.toJsonStr(bizCoupon)
                );
                // 确保日期已添加到日期集合中
                redisTemplate.opsForSet().add(ALL_COUPON_DATES_KEY, dateFormat);
                log.info("新增优惠券到 Redis，ID: {}, 日期: {}", bizCoupon.getId(), dateFormat);
            }
            return "新增优惠券成功";
        }catch (CustomException e){
            log.error("新增优惠券失败", e);
            throw new CustomException("新增优惠券失败" + e.getMsg());
        }
    }

    /**
     * 更新优惠券功能，判断优惠券是否合法，合法则插入数据库，同时判断是否属于今天的秒杀活动，如果是则更新redis
     * @param bizCoupon
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateCoupon(BizCoupon bizCoupon) {
        try {
            //校验优惠券是否合法
            isValidCoupon(bizCoupon);
            //1.更新数据库
            bizCouponMapper.updateByPrimaryKey(bizCoupon);
            //2.更新redis
            if (isTodayFlashSale(bizCoupon)) {
                //获取优惠券的日期
                String dateFormat = DateUtil.format(bizCoupon.getInstantStart(), DATE_FORMAT);
                //判断该日期是否属于今天的秒杀活动
                String redisKey = BIZ_COUPON_SECKILL_KEY + dateFormat;
                //如果属于今天的秒杀活动，更新redis
                redisTemplate.opsForHash().put(
                        redisKey,
                        String.valueOf(bizCoupon.getId()),
                        JSONUtil.toJsonStr(bizCoupon)
                );
                log.info("更新优惠券到 Redis，ID: {}, 日期: {}", bizCoupon.getId(), dateFormat);
            }
            return "更新优惠券成功";
        }catch (CustomException e){
            log.error("更新优惠券失败", e);
            throw new CustomException("更新优惠券失败:" + e.getMsg());
        }
    }


    /**
     * 删除优惠券功能，判断优惠券是否合法，合法则插入数据库，同时判断是否属于今天的秒杀活动，如果是则更新redis
     * @param bizCouponId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String deleteCoupon(Long bizCouponId) {
        try {
            //根据id查询优惠券
            BizCoupon bizCoupon = bizCouponMapper.selectByPrimaryKey(bizCouponId);
            if (bizCoupon == null) {
                throw new CustomException("优惠券不存在");
            }
            //1.删除数据库
            bizCouponMapper.deleteByPrimaryKey(bizCouponId);
            //2.删除redis
            if (isTodayFlashSale(bizCoupon)) {
                //获取优惠券的日期
                String dateFormat = DateUtil.format(bizCoupon.getInstantStart(), DATE_FORMAT);
                //判断该日期是否属于今天的秒杀活动
                String redisKey = BIZ_COUPON_SECKILL_KEY + dateFormat;
                //直接删除redis
                redisTemplate.opsForHash().delete(
                        redisKey,
                        String.valueOf(bizCouponId)
                );
                log.info("当天秒杀优惠券缓存已空，清理RedisKey: {} 及日期: {}", redisKey, dateFormat);
            } else {
                log.info("非当天秒杀优惠券，无需清理Redis");
            }
            return "删除优惠券成功";
        } catch (CustomException e){
            log.error("优惠券删除失败", e);
            throw new CustomException("优惠券删除失败:" + e.getMsg());
        }
    }


    /**
     * 根据主体id查询优惠券：主要是商家券,进入商家详情页时，查询该商家的优惠券
     * @param entityId
     * @return
     */
    @Override
    public List<BizCoupon> getCouponByEntityId(Long entityId) {
        try {
            //传入当前的时间，判断优惠券是否已经过期
            String today = DateUtil.format(new Date(), DATE_FORMAT_2);
            // TODO 有一点问题，我需要知道传的商家ID是多少，我现在拿到的这个商家id是怎么来的？
            List<BizCoupon> bizCoupons = bizCouponMapper.getCouponByEntityId(entityId , today);
            if (bizCoupons.isEmpty()) {
                throw new CustomException("该商家没有优惠券");
            }
            return bizCoupons;
        }catch (CustomException e){
            throw new CustomException("查询商家优惠券失败，请稍后重试！");
        }
    }

    /**
     * 平台使用： 用来管理优惠券
     * 根据类型查询优惠券：分为平台券和商家券 0:平台券 1:商家券
     * @param type
     * @return
     */
    @Override
    public List<BizCoupon> getCouponByType(int type) {
        try {
            //传入当前的时间，判断优惠券是否已经过期
            String today = DateUtil.format(new Date(), DATE_FORMAT_2);
            List<BizCoupon> bizCoupons = bizCouponMapper.getCouponByType(type, today);
            if (bizCoupons.isEmpty()) {
                throw new CustomException("当前没有优惠券");
            }
            return bizCoupons;
        }catch (CustomException e){
            throw new CustomException("查询优惠券失败，请稍后重试！");
        }
    }

    /**
     * 根据时间查询优惠券，每天凌晨将今天抢购的优惠券加入到redis中
     * @param date
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public List<BizCoupon> queryCouponByTime(String date , int page, int pageSize) {
        return bizCouponMapper.selectCouponByTime(date , page, pageSize);
    }

    @Override
    public BizCoupon getCouponById(Long id) {
        BizCoupon bizCoupon = bizCouponMapper.selectByPrimaryKey(id);
        if (bizCoupon == null) {
            throw new CustomException("优惠券不存在");
        }
        if (bizCoupon.getStatus() == "0") {
            throw new CustomException("优惠券未生效");
        }
        if (bizCoupon.getStatus() == "2") {
            throw new CustomException("优惠券已过期");
        }
        if (bizCoupon.getStatus() == "3") {
            throw new CustomException("优惠券已停用");
        }
        // 判断结束时间是否小于当前时间
        if (bizCoupon.getValidEnd().before(new Date())) {
            throw new CustomException("优惠券已过期");
        }
        return bizCoupon;
    }

    /**
     * 降级方法：返回缓存中的旧数据或空列表
     * @param redisKey
     * @param emptyCacheKey
     * @return
     */
    private List<BizCoupon> fallbackToListCoupons(String redisKey, String emptyCacheKey) {
        try {
            // 尝试返回缓存中的旧数据（如果有）
            if (redisTemplate.hasKey(redisKey)) {
                List<Object> couponList = redisTemplate.opsForHash().values(redisKey);
                return couponList.stream()
                        .map(couponJson -> JSONUtil.toBean((String) couponJson, BizCoupon.class))
                        .collect(Collectors.toList());
            }

            // 检查是否有空缓存标记
            if (redisTemplate.hasKey(emptyCacheKey)) {
                return Collections.emptyList();
            }

            // 没有可用缓存，返回空列表或默认数据
            log.warn("降级返回空优惠券列表");
            return Collections.emptyList();
        } catch (Exception e) {
            log.error("降级方案异常", e);
            return Collections.emptyList();
        }
    }



    /**
     * 校验优惠券是否合法
     * @param bizCoupon
     */
    public void isValidCoupon(BizCoupon bizCoupon) {
        if (bizCoupon == null) {
            throw new CustomException("优惠券信息不能为空");
        }
        // 检查 卡券标题 是否为空
        if (bizCoupon.getTitle() == null || bizCoupon.getTitle().isEmpty()) {
            throw new CustomException("卡券标题不能为空");
        }
        // 检查 卡券金额 是否为空 或小于等于 0
        if (bizCoupon.getAmount() == null || bizCoupon.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new CustomException("卡券金额不合法");
        }
        // 检查 使用门槛金额 是否为空 或小于等于 0
        if (bizCoupon.getMinAmount() == null || bizCoupon.getMinAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new CustomException("使用门槛金额不合法");
        }
        // 判断 卡券金额 是否小于等于 使用门槛金额
        if (bizCoupon.getAmount().compareTo(bizCoupon.getMinAmount()) > 0) {
            throw new CustomException("卡券金额不能大于使用门槛金额");
        }
        // 判断 isFlashSale 是否为 null
        if (bizCoupon.getIsFlashSale() == null) {
            throw new CustomException("卡券参与秒杀活动不能为空");
        }
        // 查看 卡券是否参与秒杀活动
        if (bizCoupon.getIsFlashSale() == 0) {
            log.info("卡券参与秒杀活动");
            // 检查 秒杀开始时间 是否为空
            if (bizCoupon.getInstantStart() == null) {
                throw new CustomException("秒杀开始时间不能为空");
            }
            // 秒杀优惠券库存固定为1
            bizCoupon.setStock(1);
        }else {
            log.info("卡券不参与秒杀活动");
            // 检查 卡券库存 是否为空 或小于等于 0
            if (bizCoupon.getStock() == null || bizCoupon.getStock() <= 0) {
                throw new CustomException("卡券库存不合法");
            }
        }
        // 检查 有效期开始时间 是否为空
        if (bizCoupon.getValidStart() == null) {
            throw new CustomException("有效期开始时间不能为空");
        }
        // 检查 有效期结束时间 是否为空
        if (bizCoupon.getValidEnd() == null) {
            throw new CustomException("有效期结束时间不能为空");
        }
        // 检查 有效期开始时间 是否早于 有效期结束时间
        if (bizCoupon.getValidStart().after(bizCoupon.getValidEnd())) {
            throw new CustomException("有效期开始时间不能晚于有效期结束时间");
        }
        // 检查 结束时间 和现在时间 比较
        if (bizCoupon.getValidEnd().before(new Date())) {
            throw new CustomException("有效期结束时间不能早于当前时间");
        }
        // 检查 卡券限制数量 是否为空 或小于等于 0
        if (bizCoupon.getLimitPerUser() == null || bizCoupon.getLimitPerUser() <= 0) {
            throw new CustomException("卡券限制数量不合法");
        }
        //根据时间判断优惠券的状态
        if (bizCoupon.getValidStart().before(new Date()) && bizCoupon.getValidEnd().after(new Date())) {
            //已经开始，未过期
            bizCoupon.setStatus("1");
            return;
        }
        if (bizCoupon.getValidStart().after(new Date())) {
            //未开始
            bizCoupon.setStatus("0");
        }
        // 主体id 不能为空
        if (bizCoupon.getEntityId() == null) {
            throw new CustomException("发布主体ID不能为空");
        }
        // 检查主体信息是否为空
        if (bizCoupon.getEntity() == null) {
            throw new CustomException("发布主体不能为空");
        }
        if (bizCoupon.getEntity() == 0) {
            bizCoupon.setType(0); // 平台券
        } else if (bizCoupon.getEntity() == 1) {
            bizCoupon.setType(1); // 商家券
        } else {
            throw new CustomException("发布主体类型不合法");
        }
    }

    /**
     * 判断是否为当天的秒杀优惠券
     * @param bizCoupon 优惠券对象
     * @return true=当天秒杀，false=非当天或非秒杀
     */
    private boolean isTodayFlashSale(BizCoupon bizCoupon) {
        // 非秒杀活动直接返回false
        if (bizCoupon.getIsFlashSale() != 0) {
            return false;
        }

        // 秒杀日期为当天（格式：yyyy-MM-dd）
        String instantStartDate = DateUtil.format(bizCoupon.getInstantStart(), DATE_FORMAT);
        String today = DateUtil.format(new Date(), DATE_FORMAT);

        return Objects.equals(instantStartDate, today);
    }

}
